//@author A0099223B

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;

public class ConsoleManager {

	private static final String CONSOLE_INPUT_LABEL_TEXT = ">> ";
	private static final Color CONSOLE_FONT_COLOR = new Color(184, 205, 239);
	private static final Color CONSOLE_BG_COLOR = new Color(30, 30, 30);
	private static final int CONSOLE_OUTPUT_ROWS = 15;
	private static final int CONSOLE_OUTPUT_COLUMNS = 90;
	private static final int CONSOLE_INPUT_COLUMNS = 50;

	private static final Font OUTPUT_FONT = new Font("Cambria", Font.PLAIN, 15);
	private static final Font INPUT_FONT = new Font("Cambria", Font.PLAIN, 20);
	private static final String EMPTY_STRING = "";
	private static final char NEWLINE_CHAR = '\n';
	private static final int ARRAY_SIZE_AND_LAST_ELEMENT_OFFSET = 1;
	private static final int LOWEST_HISTORY_POSITION = 0;

	private static final String ACTION_DOWN_PRESSED = "downPressed";
	private static final String KEYSTROKE_DOWN = "DOWN";

	private static final String ACTION_UP_PRESSED = "upPressed";
	private static final String KEYSTROKE_UP = "UP";

	private static JPanel consolePanel = null;
	private static JTextField consoleInput = new JTextField(
			CONSOLE_INPUT_COLUMNS);
	private static JTextArea consoleOutput = new JTextArea(CONSOLE_OUTPUT_ROWS,
			CONSOLE_OUTPUT_COLUMNS);
	private static ArrayList<String> inputHistory = new ArrayList<String>();
	private static int inputHistoryPosition = LOWEST_HISTORY_POSITION;
	private static String unenteredInput = EMPTY_STRING;

	public static JPanel getConsolePanel() {
		if (consolePanel == null) {
			initialisePanel();
		}
		assert (consolePanel != null);
		return consolePanel;
	}

	private static void initialisePanel() {
		configureComponents();
		setPanelLayout();
		setPanelTitle();
		addComponentsToPanel();
	}

	private static void configureComponents() {
		configureConsoleOutput();
		configureConsoleInput();
	}

	private static void configureConsoleOutput() {

		consoleOutput.setEditable(false);
		consoleOutput.setLineWrap(true);
		consoleOutput.setBackground(CONSOLE_BG_COLOR);
		consoleOutput.setForeground(CONSOLE_FONT_COLOR);
		consoleOutput.setFont(OUTPUT_FONT);
	}

	private static void configureConsoleInput() {
		consoleInput.setFont(INPUT_FONT);
		consoleInput.setMaximumSize(consoleInput.getPreferredSize());
		consoleInput.setBackground(CONSOLE_BG_COLOR);
		consoleInput.setForeground(CONSOLE_FONT_COLOR);
		consoleInput.setCaretColor(CONSOLE_FONT_COLOR);
		setActionListener(consoleInput);
		setUpKeyAction(consoleInput);
		setDownKeyAction(consoleInput);
	}

	private static void setActionListener(JTextField textField) {
		textField.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				setInputHistoryVariables();
				startProcessingUserInput();
			}

		});

	}

	private static void setInputHistoryVariables() {
		assert (inputHistoryPosition >= 0);
		if (isNotOldInput() && isNotEmptyInput()) {
			addInputToHistory();
		}
		resetInputHistoryPosition();
	}

	private static boolean isNotOldInput() {
		boolean isNotOldInput = true;
		isNotOldInput = isNotOldInput && isNotEditedFromOld();
		isNotOldInput = isNotOldInput && isNotSameAsLastInput();
		return isNotOldInput;
	}

	private static boolean isNotEditedFromOld() {
		if (inputHistoryPosition < inputHistory.size()) {
			if (consoleInput.getText().equals(
					inputHistory.get(inputHistoryPosition))) {
				return false;
			}
		}

		return true;
	}

	private static boolean isNotSameAsLastInput() {
		if (!inputHistory.isEmpty()) {
			if (consoleInput.getText().equals(
					inputHistory.get(inputHistory.size()
							- ARRAY_SIZE_AND_LAST_ELEMENT_OFFSET))) {
				return false;
			}
		}
		return true;
	}

	private static boolean isNotEmptyInput() {
		String input = consoleInput.getText();
		return !input.trim().equals(EMPTY_STRING);
	}

	private static void addInputToHistory() {
		inputHistory.add(consoleInput.getText());
	}

	private static void resetInputHistoryPosition() {
		inputHistoryPosition = inputHistory.size();
		unenteredInput = EMPTY_STRING;
	}

	private static void startProcessingUserInput() {
		String userInput = consoleInput.getText();
		emptyConsoleInput();
		printCommandFeedback(userInput);
		MainUi.processUserInput(userInput);
	}

	private static void emptyConsoleInput() {
		consoleInput.setText(EMPTY_STRING);
	}

	private static void printCommandFeedback(String userInput) {
		printMessage(CONSOLE_INPUT_LABEL_TEXT + userInput);
	}

	public static void printMessage(String message) {
		consoleOutput.append(message + NEWLINE_CHAR);
		consoleOutput.setCaretPosition(consoleOutput.getDocument().getLength());
	}

	public static void setFocus() {
		consoleInput.requestFocusInWindow();
	}

	private static void setUpKeyAction(JTextField textField) {
		textField.getInputMap().put(KeyStroke.getKeyStroke(KEYSTROKE_UP),
				ACTION_UP_PRESSED);
		textField.getActionMap().put(ACTION_UP_PRESSED, new AbstractAction() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				recordUnenteredInput();
				goUpInHistoryPosition();
				setTextFromInputHistory();
			}

		});
	}

	private static void recordUnenteredInput() {
		if (inputHistoryPosition == inputHistory.size()) {
			unenteredInput = consoleInput.getText();
		}
	}

	// Pressing up traverse backwards in the inputHistory ArrayList
	private static void goUpInHistoryPosition() {
		if (inputHistoryPosition > LOWEST_HISTORY_POSITION) {
			inputHistoryPosition--;
		}
	}

	private static void setDownKeyAction(JTextField textField) {
		textField.getInputMap().put(KeyStroke.getKeyStroke(KEYSTROKE_DOWN),
				ACTION_DOWN_PRESSED);
		textField.getActionMap().put(ACTION_DOWN_PRESSED, new AbstractAction() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				recordUnenteredInput();
				goDownInHistoryPosition();
				setTextFromInputHistory();

			}

		});
	}

	private static void goDownInHistoryPosition() {
		if (inputHistoryPosition < inputHistory.size()) {
			inputHistoryPosition++;
		}

	}

	private static void setTextFromInputHistory() {
		assert (inputHistoryPosition >= LOWEST_HISTORY_POSITION && inputHistoryPosition <= inputHistory
				.size());

		if (inputHistoryPosition == inputHistory.size()) {
			consoleInput.setText(unenteredInput);
		} else {
			consoleInput.setText(inputHistory.get(inputHistoryPosition));
		}

	}

	private static void setPanelLayout() {
		consolePanel = new JPanel();
		consolePanel.setLayout(new BoxLayout(consolePanel, BoxLayout.Y_AXIS));

	}

	private static void setPanelTitle() {
		consolePanel.setBorder(BorderFactory.createTitledBorder("Console"));
	}

	private static void addComponentsToPanel() {
		consolePanel.add(createConsoleOutputScrollPane());
		consolePanel.add(createConsoleInputPanel());
	}

	private static Component createConsoleInputPanel() {
		JPanel consoleInputPanel = new JPanel();
		consoleInputPanel.setLayout(new BoxLayout(consoleInputPanel,
				BoxLayout.X_AXIS));
		consoleInputPanel.add(new JLabel(CONSOLE_INPUT_LABEL_TEXT));
		consoleInputPanel.add(consoleInput);

		return consoleInputPanel;
	}

	private static JScrollPane createConsoleOutputScrollPane() {
		return new JScrollPane(consoleOutput);
	}

}
